package models

import (
	"database/sql"
	"fmt"
	"strconv"
	"strings"
	"time"
)

type SNpc列表 struct {
	Id    int32             `xorm:"autoincr"` //uid  int
	V英文名称 string            `xorm:"'英文名称'"`
	V中文名称 string            `xorm:"'中文名称'"`
	V在线时间 map[string]string `xorm:"'在线时间'"`
	X     int32
	Y     int32
	Z     int32
	V方向   float32 `xorm:"'方向'"`
	V类型   string  `xorm:"'类型'"`
	V欢迎语  string  `xorm:"'欢迎语'"`
	V大小   float32 `xorm:"'大小'"`
}

func (z *SNpc列表) TableName() string {
	return "npc列表"
}

type S状态_交易 struct {
	NPC *SNpc列表 //当前对哪个NPC交易
	菜单  uint16  //选择的是哪个菜单
}

//发起对NPC的对话
func (rt *RunTime) Func发起对话NPC(游戏NPCID int32, 关键字 string) {
	m := rt.V角色内容
	NPC内容 := m.F获取身边内容(游戏NPCID)
	if NPC内容 == nil {
		rt.消息_系统消息(3, "距离过远")
		return
	}
	npc := NPC内容.V内容.(*SNpc列表)
	index := 0
	if 关键字 == "" { //用户第一次询问NPC
		rt.V角色.V状态 = E角色状态_NPC
		rt.V角色.V状态内容 = &S状态_交易{
			NPC: npc,
			菜单:  0,
		}
		NPC菜单 := NPC_获取菜单(npc.Id, 0, 10, false)
		rt.NPC_显示菜单(npc, 游戏NPCID, npc.V欢迎语, NPC菜单)
		return
	}
	菜单ID, err := strconv.Atoi(关键字)
	if err != nil {
		if len(关键字) > 11 && 关键字[:11] == "/ClanCreate" {
			行会名称 := 关键字[11:]
			if rt.V角色.V金币 < 1000000 {
				rt.消息_系统消息(3, "想要申请行会,没有100万金币是无法申请的")
				return
			}
			if rt.执行脚本("/创建行会 "+行会名称, 游戏NPCID) == true {
				rt.V角色.V金币 -= 1000000
			}
			return
		} else {
			rt.消息_系统消息(3, "被选择菜单不为数字")
			return
		}
	}
	rt.V角色.V状态内容 = &S状态_交易{
		NPC: npc,
		菜单:  uint16(菜单ID),
	}
	菜单 := NPC_获取菜单(npc.Id, uint16(菜单ID), 1, false)[0]
	//再次检测是否可以点击这个菜单
	//检测完成点击这个菜单,需要消耗哪些物品
	for k, v := range 菜单.V消耗物品 {
		if err := rt.脚本_查询条件(k, v); err != nil {
			rt.消息_系统消息(3, err.Error())
			return
		}
	}
	//需要注意,凡是return的,说明不需要消耗,如果没有return,则表示操作成功,需要删除物品
	脚本S := strings.Split(菜单.V脚本内容, "\n")
	for i := 0; i < len(脚本S); i++ {
		脚本 := 脚本S[i]
		分割 := strings.Split(脚本, " ")
		switch 分割[0] {
		case "/商店":
			商店ID := int32(0)
			if t, err := strconv.Atoi(分割[1]); err != nil {
				rt.消息_系统消息(3, "商店ID必须为数字")
				return
			} else {
				商店ID = int32(t)
			}
			商店, err := NPC_获取商店(商店ID)
			if err != nil {
				rt.消息_系统消息(3, "商店ID不正确:"+err.Error())
				return
			}
			tx := CreateBuff(100)
			tx.AddInt16(0x00A0)
			tx.AddInt32(游戏NPCID)
			bp := len(tx.Buff)
			tx.AddInt32(0)
			if len(商店.V出售1标题) != 0 {
				tx.AddStringAndLen(商店.V出售1标题)
				index++
			}
			if len(商店.V出售2标题) != 0 {
				tx.AddStringAndLen(商店.V出售2标题)
				index++
			}
			if len(商店.V出售3标题) != 0 {
				tx.AddStringAndLen(商店.V出售3标题)
				index++
			}
			tx.Buff[bp] = byte(index)
			rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
		case "/菜单":
			菜单ID, err := strconv.Atoi(分割[1])
			if err != nil {
				rt.消息_系统消息(3, "菜单ID必须为数字")
				return
			}
			菜单S := NPC_获取菜单(npc.Id, uint16(菜单ID), 10, false)
			if len(分割) == 2 {
				rt.NPC_显示菜单(npc, 游戏NPCID, npc.V欢迎语, 菜单S)
			} else {
				rt.NPC_显示菜单(npc, 游戏NPCID, 分割[2], 菜单S)
			}
		default:
			if rt.执行脚本(脚本, 游戏NPCID) == false {
				return
			}
		}
	}
	for k, v := range 菜单.V消耗物品 {
		rt.脚本_执行条件(k, v)
	}

}

var m_NPC菜单 map[int32]map[uint16]*StructNPC菜单

//功能 菜单指定NPC的菜单
func NPC_获取菜单(npcid int32, index uint16, lenght uint16, 全部 bool) []*StructNPC菜单 {
	var ret []*StructNPC菜单
	var 总菜单 map[uint16]*StructNPC菜单
	if m_缓存表 == true {
		if t, ok := m_NPC菜单[npcid]; ok == true {
			总菜单 = t
		}
	} else {
		总菜单 = make(map[uint16]*StructNPC菜单)
		session := db.Where("npcid=?", npcid).OrderBy("排序 ASC")
		//domain := fmt.Sprintf("[NpcID,=,%d]", npcid)
		var revs []*StructNPC菜单
		if err := session.Find(&revs); err == nil {
			for _, v := range revs {
				v.V脚本内容 = strings.Replace(v.V脚本内容, "\r", "", -1)
				总菜单[v.V排序] = v
			}
		}
	}
	if 全部 == false {
		for i := uint16(0); i < lenght; i++ {
			if v, ok := 总菜单[index+i]; ok == false {
				break
			} else {
				ret = append(ret, v)
			}
		}
	} else {
		for _, v := range 总菜单 {
			ret = append(ret, v)
		}
	}
	return ret
}

var m_商店列表 map[int32]*StructNpc商店
var 随机物品 int

func NPC_获取商店(id int32) (*StructNpc商店, error) {
	ret := &StructNpc商店{Id: id}
	if id == -1 { //如果是-1，则表示是随机内容
		ret.V出售1标题 = "ETC"
		cnt := 0
		已放置 := 0
		for _, v := range m_系统物品列表2 {
			物品 := m_物品信息[v.V信息ID]
			if 物品 == nil {
				fmt.Println("发现未知的物品:" + v.V中文名称)
				continue
			}
			if 物品.V英文名称 != v.V中文名称 {
				continue
			}
			cnt++
			if cnt < 随机物品 {
				continue
			}
			if len(ret.V出售1清单) != 0 {
				ret.V出售1清单 += ","
			}
			ret.V出售1清单 += v.V中文名称
			已放置++
			if 已放置 >= 10 { //每次显示10个
				break
			}
		}
		随机物品 += 已放置
		if 已放置 != 10 {
			随机物品 = 0
		}
		return ret, nil
	}
	if m_缓存表 == true {
		if t, ok := m_商店列表[id]; ok == true {
			return t, nil
		}
	}

	if ok, _ := db.Get(ret); ok == false {
		return nil, sql.ErrNoRows
	}
	if m_缓存表 == true {
		if m_商店列表 == nil {
			m_商店列表 = make(map[int32]*StructNpc商店)
			m_商店列表[id] = ret
		}
	}
	return ret, nil
}

//功能 角色在指定NPC处选择了指定菜单
func (rt *RunTime) NPC_显示菜单(npc *SNpc列表, 游戏NpcID int32, 标题 string, 菜单S []*StructNPC菜单) {
	tx := CreateBuff(100)
	tx.AddInt16(0x0106)
	tx.AddInt32(游戏NpcID)
	tx.AddStringAndLen(npc.V英文名称)
	tx.AddStringAndLen(标题)
	tx.AddInt32(0)
	buffp := len(tx.Buff)
	tx.AddInt32(0)
	lenp := 0
	for _, 菜单 := range 菜单S { //每个菜单最多有10个选项
		//的这里加入用户的 V任务状态 是否符合菜单显示要求
		符合要求 := true
		for kk, vv := range 菜单.V完成任务标志 {
			if rt.V角色.V任务状态[kk] != vv {
				符合要求 = false
				break
			}
		}
		if 符合要求 == false {
			continue
		}
		符合要求 = true //如果指定的标志与排除中的一致，这不再显示，主要用到做过的任务就不再做了
		for kk, vv := range 菜单.V排除任务标志 {
			if vv == -2 { //如果是限时任务 -2表示用当前时间减保存的时间
				当前值, ok := rt.V角色.V任务状态[kk] //当前值为
				if ok == true {
					上次时间 := time.Unix(int64(当前值), 0)
					if time.Now().Sub(上次时间).Hours() < 24 {
						符合要求 = false
					}
				}
			} else {
				if rt.V角色.V任务状态[kk] == vv {
					符合要求 = false
					break
				}
			}
		}
		if 符合要求 == false {
			continue
		}
		lenp++
		switch 菜单.V菜单显示内容 {
		case "领取奖励":
			tx.AddStringAndLen("/领取奖励")
		case "创建行会":
			tx.AddStringAndLen("/ClanCreate")
		default:
			tx.AddStringAndLen(strconv.Itoa(int(菜单.V排序)))
		}
		tx.AddStringAndLen(菜单.V菜单显示内容)

	}
	tx.Buff[buffp] = byte(lenp)
	rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
}
func 售价(价格 int64) int64 {
	价格 *= 3
	价格 /= 10
	return int64(价格)
}

//功能 角色在指定NPC处购买指定数量的物品
func (rt *RunTime) NPC_购买物品(npcid int32, 物品ID int32, 数量 uint16) {
	if _, ok := rt.V角色内容.V场景[npcid]; ok == false {
		rt.消息_系统消息(3, "距离过远")
		return
	}
	物品 := 创建物品(物品ID)
	if 物品 == nil {
		rt.消息_系统消息(3, "没有发现物品")
		return
	}
	if 数量 == 0 {
		rt.消息_系统消息(3, "数量不正确")
		return
	}
	价格 := 物品.V售价 * int64(数量)
	if rt.V角色.V金币 < 价格 {
		rt.消息_系统消息(3, "金币不足")
		return
	}
	物品.V数量 = 数量
	是否购买 := false
	背包ID := uint8(0)
	for i := 0; i < 4; i++ {
		var err error
		背包ID = uint8(i)
		背包 := rt.背包_获取背包(背包ID)
		if 空位X, 空位Y, ok := rt.背包_查询空位(背包, 物品); ok == false {
			continue
		} else {
			if err = rt.背包_存放物品(背包, 空位X, 空位Y, 物品); err == nil {
				是否购买 = true
				rt.同步_背包指定点(背包, 空位X, 空位Y, false)
				break
			} else {
				panic("")
			}
		}
	}
	if 是否购买 == false {
		rt.消息_系统消息(3, "背包空间不足")
		return
	}
	if 价格 > 0 {
		rt.V角色.V金币 -= 价格
		rt.发送用户信息(用户信息_金币)
	}
}
func (rt *RunTime) NPC_选择菜单(npcid int32, 选择标题 string) {
	var npc *SNpc列表
	m := rt.V角色内容
	状态 := rt.V角色.V状态内容.(*S状态_交易)

	NPC内容 := m.F获取身边内容(npcid)
	if NPC内容 == nil {
		rt.消息_系统消息(3, "距离过远")
		return
	}
	npc = NPC内容.V内容.(*SNpc列表)
	//查找这个NPC是否有出售这个菜单
	var 商店菜单 *StructNpc商店
	NPC菜单 := NPC_获取菜单(npc.Id, 状态.菜单, 10, false)
	for _, 菜单 := range NPC菜单 {
		脚本S := strings.Split(菜单.V脚本内容, "\n")
		找到 := false
		for _, 脚本 := range 脚本S {
			分割 := strings.Split(脚本, " ")
			if 分割[0] == "/商店" {
				商店ID := int32(0)
				if t, err := strconv.Atoi(分割[1]); err != nil {
					rt.消息_系统消息(3, "商店ID必须为数字")
					return
				} else {
					商店ID = int32(t)
				}
				if t, err := NPC_获取商店(商店ID); err != nil {
					rt.消息_系统消息(3, "商店ID不正确")
					return
				} else {
					商店菜单 = t
				}
				找到 = true
				break
			}
		}
		if 找到 == true {
			break
		}
	}
	if 商店菜单 == nil {
		panic("可能是外挂")
	}
	var 出售列表 string
	if 商店菜单.V出售1标题 == 选择标题 {
		出售列表 = 商店菜单.V出售1清单
	} else if 商店菜单.V出售2标题 == 选择标题 {
		出售列表 = 商店菜单.V出售2清单
	} else if 商店菜单.V出售3标题 == 选择标题 {
		出售列表 = 商店菜单.V出售3清单
	}
	if len(出售列表) == 0 {
		rt.消息_系统消息(3, "不存在的选项")
		return
	}
	tx := CreateBuff(4096)
	tx.AddInt16(0x00DC)
	tx.AddStringAndLen(选择标题)
	出售列表S := strings.Split(出售列表, ",")
	tx.AddInt32(int32(len(出售列表S))) //添加数量
	for _, v := range 出售列表S {
		复制物品, err := 物品_获取系统物品列表_中文(v)
		if err != nil {
			rt.消息_系统消息(3, fmt.Sprintf("不存在的物品:%s", v))
			return
		}
		tx.AddInt32(复制物品.Id)
		价格 := float32(复制物品.V售价)
		if 商店菜单.V调控 == 0 {
			商店菜单.V调控 = 1
		}
		价格 *= 商店菜单.V调控
		价格 *= (1 + 商店菜单.V税率)
		tx.AddInt64(int64(价格))
		rt.物品_物品信息(tx, 复制物品)
	}
	rt.AddHeadSend10(0x0A, [][]byte{{}, tx.Buff})
}
